/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2019-2019.
 * Description: dts reg operation language support
 * Author: xiaowei
 * Create: 2019-07-18
 */

#include <linux/hal/drol.h>

#define DROL_PFN(x)		((x) >> 12)
#define DROL_DELAY_LOOP		1000

static inline void drol_delay(int delay)
{
	volatile int i;

	for (i = 0; i < delay * DROL_DELAY_LOOP; i++)
		;
}

void drol_unmap_op(struct drol_op *op, int op_size, drol_unmap unmap)
{
	int i;

	if (!op || !unmap)
		return;

	for (i = 0; i < op_size; i++) {
		if (!op[i].reuse)
			unmap(op[i].addr);
	}
}

int drol_map_op(struct drol_op *op, int op_size, drol_map map, drol_unmap unmap)
{
	int i, j;

	if (!op)
		return -EINVAL;

	if (!map) {
		for (i = 0; i < op_size; i++)
			op[i].addr = (void *)(uintptr_t)op[i].reg;
		return 0;
	}

	if (!unmap)
		return -EINVAL;

	for (i = 0; i < op_size; i++) {
		op[i].reuse = 0;
		for (j = i - 1; j >= 0; j--) {
			if (DROL_PFN(op[i].reg) == DROL_PFN(op[j].reg)) {
				op[i].reuse = 1;
				op[i].addr = op[j].addr + op[i].reg - op[j].reg;
				break;
			}
		}

		if (op[i].reuse)
			continue;

		op[i].addr = map(op[i].reg);
		if (!op[i].addr) {
			drol_unmap_op(op, i, unmap);
			return -ENOMEM;
		}
	}
	return 0;
}

void drol_do_op(struct drol_op *op, int op_size)
{
	int i;
	unsigned int val;

	if (!op)
		return;

	for (i = 0; i < op_size; i++) {
		val = 0;
		drol_delay(op[i].delay);

		if (op[i].op & DROL_OP_READ)
			val = readl(op[i].addr);

		if (!(op[i].op & DROL_OP_WRITE))
			continue;

		val &= ~op[i].clear;
		val |= op[i].set;
		val ^= op[i].xor;

		writel(val, op[i].addr);
	}
}

